<template>
  <div class="option">
    <div class="option-header">
      <div>选项式Store</div>

      <div>
        <button @click="optionStore.increment()">递增</button>

        <!-- mapState映射为只读计算属性 -->
        <button @click="number++">mapState递增</button>

        <!-- mapWritableState映射为可变计算属性 -->
        <button @click="mapWritableStateNumber++">mapWritableState递增</button>

        <!-- $reset用于重置State为初始值 -->
        <button @click="optionStore.$reset()">重置</button>

        <!-- $patch用于同时变更多个State -->
        <button @click="patch">变更</button>

        <!-- $subscribe用于监听State的变化 -->
        <button @click="subscribe">订阅State</button>
        <button @click="unsubscribe()">不订阅State</button>

        <!-- $onAction用于监听Action和结果的变化 -->
        <button @click="asynchronous">调用action</button>
        <button @click="onAction">订阅Action</button>
        <button @click="unOnAction()">不订阅Action</button>
      </div>
    </div>

    <div class="option-body">
      <div class="option-body__item" v-for="item in lists">
        <div class="option-body__label">{{ item.label }}</div>
        <div class="option-body__value">{{ item.value }}</div>
      </div>
    </div>
  </div>
</template>

<script>
import {defineComponent} from 'vue';
import {mapActions, mapState, mapStores, mapWritableState} from 'pinia';
import {useOptionStore} from '@/stores/option.js';

export default defineComponent({
  name: 'Option',
  data() {
    return {
      unsubscribe: null,
      unOnAction: null,
    };
  },
  computed: {
    lists() {
      return [
        {
          label: 'mapStores',
          value: this.optionStore.number,
        },
        {
          label: 'mapState[]',
          value: this.number,
        },
        {
          label: 'mapState{}',
          value: this.mapStateNumber,
        },
        {
          label: 'mapWritableState',
          value: this.mapWritableStateNumber,
        },
      ];
    },

    // mapStores后通过idStore的形式访问，后缀Store可在main.js中通过setMapStoreSuffix('xxx')自定义
    ...mapStores(useOptionStore),

    // mapState辅助函数将state属性映射为只读计算属性。为方便迁移getter也使用mapState，而非mapGetters
    ...mapState(useOptionStore, ['number', 'encrypt']),
    ...mapState(useOptionStore, {
      mapStateString: store => store.string,
      mapStateNumber: store => store.number,
      mapStateBoolean: store => store.boolean,
      mapStateArray: store => store.array,
      mapStateObject: store => store.object,

      mapStateUseOther: store => store.useOther,
      mapStatePassParams: store => store.passParams,
    }),

    // mapWritableState辅助函数将state属性映射为可变计算属性
    ...mapWritableState(useOptionStore, {
      mapWritableStateNumber: 'number',
    }),
  },
  methods: {
    // mapActions辅助函数将action属性映射为组件方法
    ...mapActions(useOptionStore, ['asynchronous']),
    ...mapActions(useOptionStore, {
      mapActionsIncrement: 'increment',
      mapActionsUseOthers: 'useOthers',
    }),

    patch() {
      // $patch用于同时变更多个State，允许将多个变更归入Devtools的同一条目中
      this.optionStore.$patch({
        string: 'TNT',
        number: 20,
      });

      this.optionStore.$patch((state) => {
        state.boolean = false;
        state.array.push('D');
      });

      console.log('$patch', this.optionStore.string); // TNT
      console.log('$patch', this.optionStore.number); // 20
      console.log('$patch', this.optionStore.boolean); // false
      console.log('$patch', this.optionStore.array); // Proxy(Array) {0: 'A', 1: 'B', 2: 'C', 3: 'D'}
    },
    subscribe() {
      // $subscribe用于监听State的变化，与watch不同的是$subscribe只在$patch后触发一次。组件被卸载时订阅被删除，若不想删除可将参数二设置为{detached: true}
      this.unsubscribe = this.optionStore.$subscribe((mutation, state) => {
        console.log('监听到变化');
      });
    },
    onAction() {
      // $onAction用于监听Action和结果的变化。组件被卸载时订阅被删除，若不想删除可将参数二设置为true
      this.unOnAction = this.optionStore.$onAction(({name, store, args, after, onError}) => {
        // name表示action名称、store表示Store实例、args表示传递给action的参数数组
        console.log('action前触发', name); // asynchronous

        // after表示action成功并完全运行后触发
        after((result) => {
          console.log('action成功并完全运行后触发', result); // undefined
        });

        // onError表示action异常或返回拒绝Promise触发
        onError((error) => {
          console.log('action异常或返回拒绝Promise触发', error);
        });
      });
    },
  },
  created() {
    console.log('mapStores', this.optionStore.string); // SYX
    console.log('mapStores', this.optionStore.number); // 10
    console.log('mapStores', this.optionStore.boolean); // true
    console.log('mapStores', this.optionStore.array); // Proxy(Array) {0: 'A', 1: 'B', 2: 'C'}
    console.log('mapStores', this.optionStore.object); // Proxy(Object) {name: 'SYX', age: 19}

    console.log('mapState', this.number); // 10
    console.log('mapState', this.mapStateString); // SYX
    console.log('mapState', this.mapStateNumber); // 10
    console.log('mapState', this.mapStateBoolean); // true
    console.log('mapState', this.mapStateArray); // Proxy(Array) {0: 'A', 1: 'B', 2: 'C'}
    console.log('mapState', this.mapStateObject); // Proxy(Object) {name: 'SYX', age: 19}

    console.log('mapWritableState', this.mapWritableStateNumber); // Proxy(Object) {name: 'SYX', age: 19}

    console.log('mapState[Getter]', this.encrypt); // 加密**SYX**加密
    console.log('mapState[Getter]', this.mapStateUseOther); // 再次加密--加密**SYX**加密--再次加密
    this.mapStatePassParams('哈哈'); // 传参成功 哈哈

    this.asynchronous(); // 异步Promise
    this.mapActionsUseOthers(); // 调用其他Store
  }
});
</script>

<style scoped>
.option-header {
  font-weight: bold;
  display: flex;
  align-items: center;
  column-gap: 20px;
  margin-bottom: 10px;

  & button {
    margin-right: 10px;
  }
}

.option-body__item {
  display: flex;
  align-items: center;
  column-gap: 20px;
  margin-top: 5px;
}

.option-body__label {
  width: 120px;
  font-size: 14px
}

.option-body__value {
  padding: 2px 10px;
  border: 1px solid gainsboro;
  font-size: 12px;
  font-weight: bold;
  color: grey;
}
</style>